/*
 * Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2006.
 *
 * Licensed under the Aduna BSD-style license.
 */
 
package edu.tju.commons.text;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class DateTimeUtil {

	private static final DateFormat timeFormat = new SimpleDateFormat("HH:mm");

	private static final long TWENTY_FOUR_HOURS = 24L * 60L * 60L * 1000L;

	private static long today0_00h = 0L;

	private static long yesterday0_00h = 0L;

	private static long halfyearago0_00h = 0L;

	private static long tomorrow0_00h = 0L;

	private static long dayaftertomorrow0_00h = 0L;

	private static long halfyearfromnow0_00h = 0L;

	private static void _initTimeVariables() {
		if (tomorrow0_00h == 0L || // first call
				System.currentTimeMillis() >= tomorrow0_00h) // a day (or
		// more) has
		// passed
		{
			Calendar calendar = Calendar.getInstance();

			calendar.setTimeInMillis(System.currentTimeMillis());
			calendar.set(Calendar.HOUR_OF_DAY, 0);
			calendar.set(Calendar.MINUTE, 0);
			calendar.set(Calendar.MILLISECOND, 0);

			today0_00h = calendar.getTimeInMillis();
			yesterday0_00h = today0_00h - TWENTY_FOUR_HOURS;
			halfyearago0_00h = today0_00h - 183 * TWENTY_FOUR_HOURS;
			tomorrow0_00h = today0_00h + TWENTY_FOUR_HOURS;
			dayaftertomorrow0_00h = tomorrow0_00h + TWENTY_FOUR_HOURS;
			halfyearfromnow0_00h = today0_00h + 183 * TWENTY_FOUR_HOURS;
		}
	}

	/**
	 * Converts the supplied 'date' to a <tt>Date</tt> object and call
	 * <tt>getNiceDateTimeString(Date)</tt>.
	 *
	 * @see #getNiceDateTimeString(long)
	 */
	public static String getNiceDateTimeString(long date) {
		return getNiceDateTimeString(new Date(date));
	}

	/**
	 * Gets a "nice" human readable string for the supplied date. This method
	 * distinguishes between the following situations:
	 * <ul>
	 * <li>Dates for today; these are shown as time only (e.g.: <b>12:15</b></li>
	 * <li>Dates for yesterday; these are shown as "Yesterday" plus the time
	 * (e.g. <b>Yesterday 16:40</b></li>
	 * <li>Dates before yesterday but no older than half a year; these are
	 * shown as a combination of month, day and time (e.g.: <b>Aug 30 16:30</b></li>
	 * <li>Dates older than half a year or dates after today; these are shown
	 * as a combination of year, month and day, but no time (e.g.: <b>Jan 1,
	 * 2001</b>).</li>
	 * </ul>
	 */
	public static String getNiceDateTimeString(Date date) {
		_initTimeVariables();

		long time = date.getTime();

		if (time >= today0_00h && time < tomorrow0_00h) {
			// today
			return timeFormat.format(date);
		}
		else if (time >= yesterday0_00h && time < today0_00h) {
			// yesterday
			return "Yesterday " + timeFormat.format(date);
		}
		else if (time >= tomorrow0_00h && time < dayaftertomorrow0_00h) {
			// tomorrow
			return "Tomorrow " + timeFormat.format(date);
		}
		else {
			// before yesterday or after tomorrow
			Calendar calendar = Calendar.getInstance();
			calendar.setTimeInMillis(time);

			String monthString = null;
			switch (calendar.get(Calendar.MONTH)) {
				case 0:
					monthString = "Jan";
					break;
				case 1:
					monthString = "Feb";
					break;
				case 2:
					monthString = "Mar";
					break;
				case 3:
					monthString = "Apr";
					break;
				case 4:
					monthString = "May";
					break;
				case 5:
					monthString = "Jun";
					break;
				case 6:
					monthString = "Jul";
					break;
				case 7:
					monthString = "Aug";
					break;
				case 8:
					monthString = "Sep";
					break;
				case 9:
					monthString = "Oct";
					break;
				case 10:
					monthString = "Nov";
					break;
				case 11:
					monthString = "Dec";
					break;
				default:
					throw new IllegalStateException("illegal month: " + calendar.get(Calendar.MONTH));
			}

			StringBuilder buffer = new StringBuilder(14);
			buffer.append(monthString);
			buffer.append(' ');
			buffer.append(calendar.get(Calendar.DAY_OF_MONTH));

			if (time >= halfyearago0_00h && time <= halfyearfromnow0_00h) {
				// Append time for dates less than half a year away
				buffer.append("  ");
				buffer.append(timeFormat.format(date));
			}
			else {
				// Append year
				buffer.append(", ");
				buffer.append(calendar.get(Calendar.YEAR));
			}

			return buffer.toString();
		}
	}

	/**
	 * Gets a "nice" human readable string for the supplied duration. Example
	 * results are:
	 * <ul>
	 * <li>duration &lt; 1 sec: <b>512ms</b></li>
	 * <li>1 sec &lt;= duration &lt; 1 min: <b>34s</b></li>
	 * <li>1 min &lt;= duration &lt; 1 hour: <b>45m</b></li>
	 * <li>1 hour &gt;= duration &lt; 1 day: <b>4h</b></li>
	 * <li>duration &gt; 1 day: <b>5d</b></li>
	 * </ul>
	 *
	 * @param duration
	 *        A time in milliseconds
	 * @return A human readable string representing the duration.
	 */
	public static String getNiceDurationString(long duration) {
		if (duration < 1000) {
			// Less than 1 second
			return String.valueOf(duration) + " ms";
		}
		else if (duration < 60 * 1000) {
			// Less than 60 seconds
			int seconds = (int) Math.round(duration / 1000d);
			if (seconds == 1) {
				return "1 second";
			}
			else {
				return seconds + " seconds";
			}
		}
		else if (duration < 60 * 60 * 1000) {
			// Less than 1 hour
			int minutes = (int) Math.round(duration / (60 * 1000d));
			if (minutes == 1) {
				return "1 minute";
			}
			else {
				return minutes + " minutes";
			}
		}
		else if (duration < 24 * 60 * 60 * 1000) {
			// Less than 1 day
			int hours = (int) Math.round(duration / (60 * 60 * 1000d));
			if (hours == 1) {
				return "1 hour";
			}
			else {
				return hours + " hours";
			}
		}
		else {
			// More than 1 day
			int days = (int) Math.round(duration / (24 * 60 * 60 * 1000d));
			if (days == 1) {
				return "1 day";
			}
			else {
				return days + " days";
			}
		}
	}
}
